<meta charset="UTF-8" />

<div id="box"></div>
<div>
	<table class="button-table">
		<tr>
			<td><button onclick="setValue(1)">1</button></td>
			<td><button onclick="setValue(2)">2</button></td>
			<td><button onclick="setValue(3)">3</button></td>
			<td><button onclick="setValue(4)">4</button></td>
			<td><button onclick="setValue(5)">5</button></td>
			<td><button onclick="changeMode()">切换模式</button></td>
		</tr>
		<tr>
			<td><button onclick="setValue(6)">6</button></td>
			<td><button onclick="setValue(7)">7</button></td>
			<td><button onclick="setValue(8)">8</button></td>
			<td><button onclick="setValue(9)">9</button></td>
			<td><button onclick="setValue(0)">清空</button></td>
			<td><button onclick="parse1()">外挂</button></td>
		</tr>
	</table>
</div>

<script>
	const mapMap = {
		null: [
			[],
			[],
			[],
			[],
			[],
			[],
			[],
			[],
			[],
		],
		0: [
			[6, 0, 0, 5, 0, 0, 0, 9, 0],
			[7, 0, 5, 0, 1, 4, 8, 0, 0],
			[4, 2, 0, 3, 6, 0, 0, 0, 0],
			[0, 4, 0, 0, 0, 0, 7, 5, 6],
			[0, 5, 8, 0, 0, 0, 9, 2, 0],
			[1, 6, 7, 0, 0, 0, 0, 4, 0],
			[0, 0, 0, 0, 9, 1, 0, 3, 4],
			[0, 0, 4, 7, 3, 0, 6, 0, 9],
			[0, 9, 0, 0, 0, 2, 0, 0, 5],
		],
		1: [
			[2, 8, 0, 9, 0, 3, 4, 6, 0],
			[0, 0, 0, 0, 0, 2, 7, 0, 0],
			[3, 9, 0, 0, 0, 0, 0, 0, 0],
			[0, 5, 0, 3, 0, 0, 8, 9, 6],
			[0, 2, 0, 0, 9, 0, 0, 7, 0],
			[9, 4, 8, 0, 0, 7, 0, 5, 0],
			[0, 0, 0, 0, 0, 0, 0, 4, 5],
			[0, 0, 4, 2, 0, 0, 0, 0, 0],
			[0, 3, 9, 4, 0, 5, 0, 8, 7],
		],
		2: [
			[9, 5, 0, 6, 7, 0, 2, 0, 0],
			[7, 0, 3, 0, 1, 2, 5, 0, 0],
			[0, 4, 0, 0, 0, 0, 6, 0, 0],
			[2, 1, 0, 3, 9, 0, 0, 0, 4],
			[0, 9, 0, 0, 0, 0, 0, 1, 0],
			[3, 0, 0, 0, 8, 5, 0, 6, 2],
			[0, 0, 9, 0, 0, 0, 0, 2, 0],
			[0, 0, 6, 2, 5, 0, 1, 0, 7],
			[0, 0, 7, 0, 6, 8, 0, 3, 5],
		],
		3: [
			[0, 0, 4, 0, 0, 5, 9, 8, 0],
			[2, 0, 0, 7, 0, 1, 5, 0, 6],
			[0, 0, 0, 8, 4, 0, 0, 3, 0],
			[4, 0, 0, 9, 0, 0, 0, 6, 0],
			[0, 6, 7, 0, 0, 0, 4, 1, 0],
			[0, 3, 0, 0, 0, 4, 0, 0, 8],
			[0, 8, 0, 0, 1, 7, 0, 0, 0],
			[7, 0, 1, 5, 0, 6, 0, 0, 3],
			[0, 2, 5, 4, 0, 0, 1, 0, 0],
		],
		4: [
			[9, 0, 0, 0, 1, 0, 0, 4, 0],
			[7, 0, 0, 4, 0, 3, 8, 0, 0],
			[0, 1, 0, 8, 0, 0, 0, 0, 7],
			[5, 8, 0, 0, 3, 0, 0, 9, 0],
			[0, 9, 1, 5, 0, 6, 7, 8, 0],
			[0, 4, 0, 0, 7, 0, 0, 1, 5],
			[4, 0, 0, 0, 0, 9, 0, 7, 0],
			[0, 0, 5, 3, 0, 4, 0, 0, 9],
			[0, 2, 0, 0, 8, 0, 0, 0, 6],
		]
	}
	const map = mapMap[4];

	let mapDate = [[[[{ readonly: true, tableValue: true, value: [], state: "" }]]]];

	let mode = false;

	let hoverItem = {
		tableTrIndex: -1,
		tableTdIndex: -1,
		trIndex: -1,
		tdIndex: -1,
		info: {
			readonly: true,
			tableValue: true,
			value: [],
			state: "",
		}
	};

	const valueTable = [
		[1, 2, 3],
		[4, 5, 6],
		[7, 8, 9]
	];

	const getSmallMapPosByIndex = function (i) {
		const index = i + 1;
		const x = i % 3 * 3;
		const y = Math.floor(i / 3) * 3;
		return { x, y };
	};

	const getSmallMapByPos = function (x, y) {
		return [
			map[y].slice(x, x + 3),
			map[y + 1].slice(x, x + 3),
			map[y + 2].slice(x, x + 3),
		]
	};

	const getSmallMapValueByPos = function (tableTrIndex, tableTdIndex, trIndex, tdIndex) {
		const table = mapDate[tableTrIndex][tableTdIndex];
		const valueList = [];
		table.forEach((tr, i) => {
			tr.forEach((td, j) => {
				if (trIndex === i && tdIndex === j) {
					return;
				}
				valueList.push(td);
			});
		});
		return valueList;
	};

	const getXAxisValueByPos = function (tableTrIndex, tableTdIndex, trIndex, tdIndex) {
		const valueList = [];
		for (let i = 0; i < 3; i++) {
			for (let j = 0; j < 3; j++) {
				if (tableTdIndex === i && tdIndex === j) {
					continue;
				}
				valueList.push(mapDate[tableTrIndex][i][trIndex][j]);
			}
		}
		return valueList;
	};

	const getYAxisValueByPos = function (tableTrIndex, tableTdIndex, trIndex, tdIndex) {
		const valueList = [];
		for (let i = 0; i < 3; i++) {
			for (let j = 0; j < 3; j++) {
				if (tableTrIndex === i && trIndex === j) {
					continue;
				}
				valueList.push(mapDate[i][tableTdIndex][j][tdIndex]);
			}
		}
		return valueList;
	};

	const makeMapDate = function () {
		const mapDateList = new Array(9).fill(null).map((_, i) => {
			const { x, y } = getSmallMapPosByIndex(i);
			const smallMap = getSmallMapByPos(x, y);
			return smallMap.map(o => o.map(a => {
				const readonly = a != 0;
				const tableValue = false;
				const value = tableValue ? new Set() : (readonly ? a : "");
				const state = "";
				return { readonly, tableValue, value, state };
			}));
		});
		mapDate = [
			mapDateList.slice(0, 3),
			mapDateList.slice(3, 6),
			mapDateList.slice(6, 9),
		];
	};

	const makeMap = function () {
		const trTable = mapDate.map((tdTable, tableTrIndex) => {
			const tdTableStrList = tdTable.map((trList, tableTdIndex) => {
				const trStrList = trList.map((tdList, trIndex) => {
					const tdStrList = tdList.map((td, tdIndex) => {
						const clickStr = `onclick="click1(${tableTrIndex},${tableTdIndex},${trIndex},${tdIndex})"`;
						const tdrReadonlyStr = td.readonly ? " normal" : "";
						const tdStateStr = td.state ? ` ${td.state}` : "";
						if (!td.tableValue) {
							return `<td ${clickStr} class="td-value${tdrReadonlyStr}${tdStateStr}">${td.value}</td>`;
						} else {
							const valueMap = td.value;
							const valueTrList = valueTable.map(valueTdList => {
								const valueTdStrList = valueTdList.map(value => {
									const valueStr = valueMap.has(value) ? value : "";
									return `<td>${valueStr}</td>`;
								});
								return `<tr>${valueTdStrList.join("")}</tr>`;
							});
							return `<td ${clickStr}><table class="value-table">${valueTrList.join("")}</table></td>`;
						}
					})
					return `<tr>${tdStrList.join("")}</tr>`;
				});
				return `<td><table class="small-table" border="1" cellspacing="0">${trStrList.join("")}</table></td>`;
			});
			return `<tr>${tdTableStrList.join("")}</tr>`;
		});
		const mapDateStr = `<table class="big-table" cellspacing="0">${trTable.join("")}</table>`;
		box.innerHTML = mapDateStr;
	};

	const parse1 = function () {
		mapDate.forEach((tdTable, tableTrIndex) => {
			tdTable.forEach((trList, tableTdIndex) => {
				trList.forEach((tdList, trIndex) => {
					tdList.forEach((td, tdIndex) => {
						if (!td.tableValue && td.value === "") {
							td.tableValue = true;
							td.value = new Set(valueTable.flat());
						}
						if (td.tableValue && td.value.size === 0) {
							td.value = new Set(valueTable.flat());
						}
					});
				});
			});
		});
		mapDate.forEach((tdTable, tableTrIndex) => {
			tdTable.forEach((trList, tableTdIndex) => {
				trList.forEach((tdList, trIndex) => {
					tdList.forEach((td, tdIndex) => {
						const x = tableTdIndex * 3 + tdIndex;
						const y = tableTrIndex * 3 + trIndex;
						if (td.tableValue) {
							const value = td.value;
							const smallTable = getSmallMapValueByPos(tableTrIndex, tableTdIndex, trIndex, tdIndex);
							const xAxisTable = getXAxisValueByPos(tableTrIndex, tableTdIndex, trIndex, tdIndex);
							const yAxisTable = getYAxisValueByPos(tableTrIndex, tableTdIndex, trIndex, tdIndex);
							const axisTable = [...xAxisTable, ...yAxisTable];
							const otherValueSet = new Set([...smallTable, ...axisTable].filter(o => !o.tableValue).map(o => o.value));
							const smallAllSet = new Set(smallTable.map(o => !o.tableValue ? o.value : [...o.value]).flat());
							const axisAllSet = new Set(axisTable.map(o => !o.tableValue ? o.value : [...o.value]).flat());
							const smallTableValue = smallTable.filter(o => o.tableValue).map(o => o.value);
							const xAxisTableValue = xAxisTable.filter(o => o.tableValue).map(o => o.value);
							const yAxisTableValue = yAxisTable.filter(o => o.tableValue).map(o => o.value);
							const twoSmallTableValue = smallTableValue.filter(o => o.size == 2);
							const twoXAxisTableValue = xAxisTableValue.filter(o => o.size == 2);
							const twoYAxisTableValue = yAxisTableValue.filter(o => o.size == 2);
							twoSmallTableValue.forEach(a => {
								twoSmallTableValue.forEach(b => {
									if (a !== b && [...a].every(o => b.has(o))) {
										makeMap();
										b.forEach(c => otherValueSet.add(c));
									}
								});
							});
							twoXAxisTableValue.forEach(a => {
								twoXAxisTableValue.forEach(b => {
									if (a !== b && [...a].every(o => b.has(o))) {
										b.forEach(c => otherValueSet.add(c));
									}
								});
							});
							twoYAxisTableValue.forEach(a => {
								twoYAxisTableValue.forEach(b => {
									if (a !== b && [...a].every(o => b.has(o))) {
										b.forEach(c => otherValueSet.add(c));
									}
								});
							});
							otherValueSet.forEach(otherValue => {
								value.delete(otherValue);
							});
							if (value.size === 1) {
								td.tableValue = false;
								td.value = [...td.value][0];
							} else {
								value.forEach(v => {
									if (!smallAllSet.has(v)) {
										td.tableValue = false;
										td.value = v;
									}
									if (!axisAllSet.has(v)) {
										td.tableValue = false;
										td.value = v;
									}
								});
							}
						}
					});
				});
			});
		});
		makeMap();
	};

	const click1 = function (tableTrIndex, tableTdIndex, trIndex, tdIndex) {
		const info = mapDate[tableTrIndex][tableTdIndex][trIndex][tdIndex];
		if (info.readonly) {
			return;
		}
		hoverItem.info.state = "";
		hoverItem.tableTrIndex = tableTrIndex;
		hoverItem.tableTdIndex = tableTdIndex;
		hoverItem.trIndex = trIndex;
		hoverItem.tdIndex = tdIndex;
		hoverItem.info = info;
		hoverItem.info.state = "active";
		makeMap();
	};

	const setValue = function (value) {
		const info = hoverItem.info;
		if (info) {
			if (info.readonly) {
				return;
			}
			if (mode) {
				if (!info.tableValue) {
					info.tableValue = true;
					info.value = new Set();
				}
				if (value === 0) {
					info.value = new Set();
				} else {
					if (info.value.has(value)) {
						info.value.delete(value);
					} else {
						info.value.add(value);
					}
				}
			} else {
				if (value === 0) {
					value = "";
				}
				info.tableValue = false;
				info.value = value;
			}
		}
		makeMap();
	}

	const changeMode = function () {
		mode = !mode;
	};

	makeMapDate();
	makeMap();
</script>

<style>
	.big-table td,
	.small-table td {
		padding: 0;
		margin: 0;
	}

	.big-table {
		text-align: center;
	}

	.small-table {
		text-align: center;
		border: 1px solid #000;
	}

	.small-table>tbody>tr>td {
		display: inline-block;
		width: 43px;
		height: 44px;
		padding: 5px;
		border: 1px solid #000;
		cursor: pointer;
	}

	.small-table .td-value {
		font-size: 36px;
	}

	.small-table .normal {
		background-color: #aaa;
	}

	.small-table .active {
		background-color: #FFFFAA;
	}

	.value-table {
		height: 100%;
		width: 100%;
		text-align: center;
	}

	.value-table td {
		display: inline-block;
		font-size: 12px;
		width: 12px;
		height: 12px;
	}

	.button-table {
		width: 500px;
		height: 70px;
	}

	.button-table button {
		display: inline-block;
		width: 80px;
		height: 100%;
		cursor: pointer;
		border: 1px solid #666;
		background-color: #CCC;
		border-radius: 4px;
		outline: none;
	}

	.button-table button:hover {
		background-color: #D4D4D4;
	}

	.button-table button:active {
		background-color: #DEDEDE;
	}
</style>